/*
 * Copyright 2012 Gregor Schauer
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package at.schauer.gregor.vienna.serializer;

import at.schauer.gregor.vienna.context.ViennaContext;
import at.schauer.gregor.vienna.context.ViennaContextAware;
import at.schauer.gregor.vienna.exception.ViennaException;

import javax.annotation.Nullable;
import java.rmi.NotBoundException;
import java.rmi.Remote;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.rmi.server.UnicastRemoteObject;
import java.util.HashMap;
import java.util.Map;

/**
 * @author Gregor Schauer
 */
public class RMISerializer<T extends Remote> implements Serializer<T>, ViennaContextAware {
	public static final String KEY = "#";
	public static final String OBJECT = "§";
	private ViennaContext ctx;
	private Registry registry;

	public RMISerializer() throws RemoteException {
		this(null, Registry.REGISTRY_PORT);
	}

	public RMISerializer(@Nullable String host, int port) throws RemoteException {
		try {
			registry = LocateRegistry.getRegistry(host, port);
		} catch (RemoteException ignored) {
			registry = LocateRegistry.createRegistry(port);
		}
	}

	@Override
	@SuppressWarnings("unchecked")
	public <E extends T> E load(Map<String, Object> map) {
		return load(map, (Class<E>) map.get("class"));
	}

	@Override
	@SuppressWarnings("unchecked")
	public <E extends T> E load(Map<String, Object> map, Class<E> clazz) {
		try {
			return (E) registry.lookup(String.valueOf(map.get(KEY)));
		} catch (Exception e) {
			throw new ViennaException(e);
		}
	}

	@Override
	@SuppressWarnings("unchecked")
	public Map<String, Object> save(T obj) {
		try {
			Class<T> type = (Class<T>) obj.getClass();
			String name = String.valueOf(ctx.getMetadataProvider(type).getIdentifier(obj));
			registry.rebind(name, UnicastRemoteObject.exportObject(obj, 0));
			Map<String, Object> map = new HashMap<String, Object>();
			map.put("class", type);
			map.put(KEY, name);
			map.put(OBJECT, obj);
			return map;
		} catch (RemoteException e) {
			throw new ViennaException(e);
		}
	}

	@SuppressWarnings("unchecked")
	public void delete(T obj) {
		try {
			Class<T> type = (Class<T>) obj.getClass();
			String name = String.valueOf(ctx.getMetadataProvider(type).getIdentifier(obj));
			registry.unbind(name);
		} catch (RemoteException e) {
			throw new ViennaException(e);
		} catch (NotBoundException e) {
			throw new ViennaException(e);
		}
	}

	@Override
	public void setViennaContext(ViennaContext viennaContext) {
		this.ctx = viennaContext;
	}
}
